package com.hwtx.form.domain.def.meta.config;

import cn.hutool.extra.spring.SpringUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hwtx.form.domain.FormConstants;
import com.hwtx.form.domain.def.FormConfigExt;
import com.hwtx.form.domain.def.meta.Api;
import com.hwtx.form.domain.def.meta.Component;
import com.hwtx.form.domain.handle.SelectItemsInfo;
import com.hwtx.form.domain.service.CommonApiInvoker;
import com.hwtx.form.domain.vo.FormData;
import com.hwtx.form.persistence.entity.ApiDef;
import com.hwtx.form.util.FormUtil;
import com.hwtx.form.util.JacksonUtil;
import io.geekidea.boot.config.properties.BootProperties;
import io.geekidea.boot.framework.exception.BusinessException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.web.ServerProperties;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.hwtx.form.domain.FormConstants.*;
import static com.hwtx.form.domain.def.meta.ChainedSelect.SOURCE;
import static com.hwtx.form.util.FormUtil.*;
import static io.geekidea.boot.generator.constant.GeneratorConstant.ID;

@Slf4j
@org.springframework.stereotype.Component
public class GroupChainSelectFormComponentConfig implements FormComponentConfig {
    public static final String WS = "ws";
    public static final String RS = "rs";

    @Resource
    private ServerProperties webApplicationContext;

    public static List<ParamMapping> getReadOutput(Map<String, Object> ext) {
        ComponentParamInfo componentParamInfo = FormComponentConfig.getParamInfo(ext.get(RS));
        if (componentParamInfo == null) {
            return Lists.newArrayList();
        }
        return componentParamInfo.getOutput();
    }

    public static String getGroupChainSelectId() {
        return "chainSelect_" + ID;
    }

    public static String getGroupChainIds() {
        return "chainSelect_" + ID + "s";
    }

    @Override
    public Map<String, Object> comExt(Map<String, Object> formData) {
        return getComponentParamInfo(formData);
    }

    @Override
    public Map<String, Object> getInputConfigParams(Map<String, Object> comExt, FormData formData) {
        FormComponentConfig.fillInputSource(formData, comExt);
        if (!Objects.equals(QUERY, getStrPostValue(formData.getData(), "type"))) {
            newParamInfo(comExt, formData, WS);
            newParamInfo(comExt, formData, RS);
        }
        return comExt;
    }

    @Override
    public FormComponentConfig.OutputConfig getOutputConfigParams(String type, FormConfigExt.FormConfigCtr formConfigCtr) {
        if (formConfigCtr == null) {
            return null;
        }
        Map<String, Boolean> comNames = Map.of(WS, true, RS, true, SOURCE, false);
        return OutputConfig.builder().params(getParamInfoCode(formConfigCtr, comNames)).notIncludeKeys(List.of("comExt")).build();
    }

    @Override
    public String getConfig(String type, Map<String, Object> formData) {
        BootProperties bootProperties = SpringUtil.getBean(BootProperties.class);
        String domain = bootProperties.getDomain();
        if (formData.containsKey(RS) || formData.containsKey(WS)) {
            List<Component> formBody = Lists.newArrayList();
            String rsCode = getStrPostValue(formData, RS);
            String wsCode = getStrPostValue(formData, WS);
            if (StringUtils.isNotBlank(rsCode)) {
                formBody.addAll(getComponentParamsConfig(rsCode, RS, formData));
            } else if (StringUtils.isNotBlank(wsCode)) {
                formBody.addAll(getComponentParamsConfig(wsCode, WS, formData));
            }
            return buildInputOutputConfig(domain, formBody, FormUtil.getUrlContext(webApplicationContext));
        }
        Map<String, ComponentParamInfo> paramInfoMap = getParamInfoMap(formData, List.of(RS, WS, SOURCE));
        List<Component> components = Lists.newArrayList();
        Component.Select rsSelect = FormComponentConfig.getApiSelect(domain, "配置", "读取服务", RS, RS, "${event.data." + RS + "}",
                Api.builder().method(HTTP_GET).url(domain + getUrlContext(webApplicationContext) + FormConstants.API_DEF_API).build(), getUrlContext(webApplicationContext));
        if (paramInfoMap.get(RS) != null) {
            rsSelect.setValue(paramInfoMap.get(RS).getCode());
        }
        components.add(rsSelect);
        Component.Select wsSelect = FormComponentConfig.getApiSelect(domain, null, "写入服务", WS, WS, "${event.data." + WS + "}",
                Api.builder().method(HTTP_GET).url(domain + getUrlContext(webApplicationContext) + FormConstants.API_DEF_API).build(), getUrlContext(webApplicationContext));
        if (paramInfoMap.get(WS) != null) {
            wsSelect.setValue(paramInfoMap.get(WS).getCode());
        }
        components.add(wsSelect);
        Component.Select source = FormComponentConfig.getSourceConfig(domain, "", getUrlContext(webApplicationContext));
        if (paramInfoMap.get(SOURCE) != null) {
            source.setValue(paramInfoMap.get(SOURCE).getCode());
        }
        source.setFormBatProp();
        components.add(source);
        return JacksonUtil.object2json(components);
    }

    @Override
    public Map<String, Object> invokeReadService(SelectItemsInfo.SelectItemInfo selectItemInfo, Map<String, Object> data, Map<String, String[]> parameters) {
        ComponentParamInfo componentParamInfo = getParamInfo(GroupChainSelectFormComponentConfig.RS, selectItemInfo.getComExt());
        if (componentParamInfo == null) {
            return null;
        }
        String code = componentParamInfo.getCode();
        ApiDef apiDef = getApiDef(code);
        if (apiDef == null) {
            throw new BusinessException("接口不存在,code = " + code);
        }
        Map<String, Object> serviceParameters = Maps.newHashMap();
        if (componentParamInfo.getInput() != null) {
            componentParamInfo.getInput().forEach(input -> {
                String name = input.getAlias();
                if (StringUtils.isBlank(name)) {
                    name = input.getKey();
                }
                serviceParameters.put(input.getKey(), parameters.get(name)[0]);
            });
        }
        Map<String, String> extraParameters = Maps.newHashMap();
        extraParameters.put(ID, getGroupChainSelectId());
        CommonApiInvoker.InvokeData invokeData = invokeService(apiDef, serviceParameters, extraParameters);
        List<String> ids = Lists.newArrayList();
        List<Map<String, Object>> readRet = Lists.newArrayList();
        if (invokeData != null && invokeData.getRet() != null && componentParamInfo.getOutput() != null) {
            invokeData.getRet().forEach(row -> {
                Map<String, Object> columns = Maps.newHashMap();
                componentParamInfo.getOutput().forEach(column -> {
                    String name = StringUtils.isNotBlank(column.getAlias()) ? column.getKey() : column.getAlias();
                    Object vv = row.get(name);
                    if (vv != null) {
                        columns.put(name, vv.toString());
                    }
                });
                columns.put(getGroupChainSelectId(), row.get(getGroupChainSelectId()));
                ids.add(row.get(getGroupChainSelectId()).toString());
                readRet.add(columns);
            });
        }
        return Map.of(selectItemInfo.getName(), readRet, getGroupChainIds(), String.join(",", ids));
    }

    @Override
    public void invokeWriteService(Map<String, Object> comExt, SelectItemsInfo.SelectItemInfo selectItemInfo, Map<String, Object> parameters) {
        ComponentParamInfo componentParamInfo = getParamInfo(WS, comExt);
        if (componentParamInfo == null || CollectionUtils.isEmpty(componentParamInfo.getInput())) {
            throw new BusinessException("无法获取写服务入参信息");
        }
        ApiDef apiDef = getApiDef(componentParamInfo.getCode());
        if (apiDef == null) {
            throw new BusinessException("接口不存在,code = " + componentParamInfo.getCode());
        }
        String key = selectItemInfo.getField();
        if (StringUtils.isNotBlank(selectItemInfo.getName())) {
            key = selectItemInfo.getName();
        }
        Object itemValue = parameters.get(key);
        Map<String, Object> param = Maps.newHashMap();
        if (itemValue instanceof Collection<?>) {
            List<String> ids = Lists.newArrayList();
            Object existIds = parameters.get(getGroupChainIds());
            List<String> existIdList = List.of();
            if (existIds != null) {
                existIdList = Lists.newArrayList(existIds.toString().split(","));
            }
            ((Collection<Map<String, Object>>) itemValue).forEach(item -> {
                componentParamInfo.getInput().forEach(input -> {
                    String name = input.getAlias();
                    if (StringUtils.isBlank(name)) {
                        name = input.getKey();
                    }
                    if (parameters.containsKey(name)) {
                        param.put(input.getKey(), parameters.get(name));
                    } else {
                        if (item.containsKey(name)) {
                            param.put(input.getKey(), item.get(name));
                        } else {
                            throw new BusinessException("无法获取服务【" + componentParamInfo.code + "】的入参【" + name + "】");
                        }
                    }
                });
                param.put(ID, item.get(getGroupChainSelectId()));
                if (item.get(getGroupChainSelectId()) != null) {
                    ids.add(item.get(getGroupChainSelectId()).toString());
                }
                invokeService(apiDef, param, Maps.newHashMap());
                param.clear();
            });
            Collection<String> delIds = CollectionUtils.subtract(existIdList, ids);
            if (CollectionUtils.isNotEmpty(delIds)) {
                invokeService(apiDef, Map.of(WR_WRITE_DEL_IDS, String.join(",", delIds)), Map.of(WR_WRITE_DEL, WR_WRITE_DEL));
            }
        }
    }

    private CommonApiInvoker.InvokeData invokeService(ApiDef apiDef, Map<String, Object> parameters, Map<String, String> extraParameters) {
        return SpringUtil.getBean(CommonApiInvoker.class).invokeApiNoCareTest(apiDef, parameters, extraParameters);
    }

    private void newParamInfo(Map<String, Object> comExt, FormData formData, String type) {
        String code = getStrPostValue(formData.getData(), type);
        if (StringUtils.isBlank(code)) {
            log.error("ChainedSelect newParamInfo code is blank");
            return;
        }
        comExt.putAll(getInputComponentParamInfo(formData.getData(), type, comExt));
    }
}